package cn.org.tpeach.nosql.redis.connection;


import cn.org.tpeach.nosql.annotation.Component;
import cn.org.tpeach.nosql.enums.LogType;
import cn.org.tpeach.nosql.enums.MsgType;
import cn.org.tpeach.nosql.exception.ServiceException;
import cn.org.tpeach.nosql.framework.BeanContext;
import cn.org.tpeach.nosql.framework.LarkFrame;
import cn.org.tpeach.nosql.framework.LogMsg;
import cn.org.tpeach.nosql.redis.bean.RedisConnectInfo;
import cn.org.tpeach.nosql.redis.command.RedisLarkContext;
import cn.org.tpeach.nosql.redis.enums.RedisStructure;
import cn.org.tpeach.nosql.redis.service.IRedisConfigService;
import cn.org.tpeach.nosql.tools.StringUtils;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import io.lettuce.core.RedisConnectionException;
import io.lettuce.core.RedisException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;

/**
 * @author tyz
 * @Title: RedisLarkFactory
 * @ProjectName RedisLark
 * @Description: RedisLark上下文创建工厂
 * @date 2019-06-23 21:11
 * @since 1.0.0
 */
@Component("redisLarkFactory")
public class RedisLarkFactory {
	final static Logger logger = LoggerFactory.getLogger(RedisLarkFactory.class);
	private IRedisConfigService redisConfigService = BeanContext.getBean("redisConfigService",IRedisConfigService.class);
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public <K, V> RedisLarkContext  connectRedis(String id) {
		RedisStructure redisStructure = null;
		RedisConnectInfo conn = null;
		LocalDateTime now = null;
		try{
			// 获取连接信息
			conn = RedisLarkPool.getConnectInfo(id);
			boolean refresh = false;
			if (conn == null) {
				conn= redisConfigService.getRedisConfigById(id);
				refresh = true;
				RedisLarkPool.addOrUpdateConnectInfo(conn);
			}
			//获取上下文
			RedisLark redisLark;
			RedisLarkContext<K,V> redisLarkContext = RedisLarkPool.getRedisLarkContext(conn.getId());
			if (redisLarkContext == null || refresh || !redisLarkContext.isConnect()) {
				redisStructure = RedisStructure.getRedisStructure(conn.getStructure());
				// 根据Structure生成对应的连接服务，并缓存到pool
				if (redisStructure == RedisStructure.UNKNOW) {
					throw new ServiceException("配置文件出错");
				}
				//TODO SSH
				Session session = null;
				if(9 == conn.getConnType()){
					session = openSSH(conn,redisStructure);
				}
				// 根据RedisStructure 配置和RedisConnectInfo 构造方法动态生成RedisLark
				Class<?> clz = Class.forName(redisStructure.getService());
//				Constructor<?> constructor = clz.getConstructor(redisStructure.getParameterTypes());
				Constructor<?> constructor = clz.getConstructor(new Class<?>[]{conn.getClass()} );
				now = LocalDateTime.now();
//				redisLark = (RedisLark) constructor.newInstance(redisStructure.getInitargs(conn));
				redisLark = (RedisLark) constructor.newInstance(new Object[]{conn});
				if(redisLarkContext == null) {
					redisLarkContext = new RedisLarkContext<K,V>(redisLark,conn);
					try {
						redisLark.ping();
					}catch (RedisException e){
						LarkFrame.larkLog.receiveMsg(LogMsg.builder()
								.logType(LogType.ERROR)
								.msgType(MsgType.RESPONSE)
								.msg(e.getMessage())
								.receiveTime(LocalDateTime.now())
								.server(conn.getName())
								.build());
						throw e;
					}
				}else {
					redisLarkContext.setRedisLark(redisLark);
					redisLarkContext.setRefresh(true);
				}
//				redisLarkContext.setSshSession(session);
				redisLarkContext.setConnect(true);
				RedisLarkPool.addRedisLarkContext(conn.getId(), redisLarkContext);
				LarkFrame.larkLog.receiveMsg(LogMsg.builder()
						.logType(LogType.INFO)
						.msgType(MsgType.REQUEST)
						.msg("AUTH")
						.receiveTime(now)
						.server(conn.getName())
						.build());
				LarkFrame.larkLog.receiveMsg(LogMsg.builder()
						.logType(LogType.INFO)
						.msgType(MsgType.RESPONSE)
						.msg("connected")
						.receiveTime(LocalDateTime.now())
						.server(conn.getName())
						.build());

			}
			return redisLarkContext;

		} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | JSchException e) {
			Throwable ex;
			if(e instanceof JSchException){
				ex = e;
			}else{
				ex = e.getCause();
			}
			if(conn !=null && !(e instanceof JSchException)){
				LarkFrame.larkLog.receiveMsg(LogMsg.builder()
						.logType(LogType.INFO)
						.msgType(MsgType.REQUEST)
						.msg("AUTH")
						.receiveTime(now == null? LocalDateTime.now():now)
						.server(conn.getName())
						.build());
				LarkFrame.larkLog.receiveMsg(LogMsg.builder()
						.logType(LogType.ERROR)
						.msgType(MsgType.RESPONSE)
						.msg("connect fail:"+ex.getMessage())
						.receiveTime(LocalDateTime.now())
						.server(conn.getName())
						.build());

			}
			if(ex instanceof RedisConnectionException){
				throw (RedisConnectionException)ex;
			}
			throw  new RedisConnectionException(ex.getMessage());
		}

	}

	private Session openSSH(RedisConnectInfo connect,RedisStructure redisStructure ) throws JSchException {
		try {
			LarkFrame.larkLog.receiveMsg(LogMsg.builder()
					.logType(LogType.INFO)
					.msgType(MsgType.REQUEST)
					.msg("正在打开SSH通道...")
					.receiveTime( LocalDateTime.now())
					.server(connect.getName())
					.build());
			JSch jsch = new JSch();
			//私钥
			if (StringUtils.isNotEmpty(connect.getSshPrivateKey())) {
				if (Files.exists(Paths.get(connect.getSshPrivateKey()))) {
					jsch.addIdentity(connect.getSshPrivateKey(), "");
				} else {
					throw new ServiceException("SshPrivateKey文件不存在");
				}
			}
			Session session = jsch.getSession(connect.getSshUser(), connect.getSshHost(),
					Integer.valueOf(connect.getSshPort()));
			session.setTimeout(3000);
			if (StringUtils.isNotEmpty(connect.getSshPassword())) {
				session.setPassword(connect.getSshPassword());
			}
			//设置第一次登陆的时候提示，可选值：(ask | yes | no)
			session.setConfig("StrictHostKeyChecking", "no");
			session.setConfig("userauth.gssapi-with-mic", "no");
			session.connect(3000);
			// 设置SSH端口转发,本地转发到远程
			int localPort = 55555;
			String remoteHost;
			Integer remotePort;
			if (redisStructure == RedisStructure.SINGLE) {
				remoteHost = connect.getHost();
				remotePort = connect.getPort();
			} else {
				String[] hosts = connect.getHost().replaceAll(";", ",").split(",");
				String[] ipport = hosts[0].split(":");
				remoteHost = ipport[0];
				remotePort = Integer.parseInt(ipport[1]);
			}
			if (StringUtils.equals(remoteHost, connect.getSshHost())) {

			}
			remoteHost = "127.0.0.1";

			int assinged_port = session.setPortForwardingL(localPort, remoteHost, remotePort);
			// 删除本地端口的转发
			// session.delPortForwardingL(localPort);
			// 断开SSH链接
			// session.disconnect();
			// 设置SSH远程端口转发,远程转发到本地
			// session.setPortForwardingR(remotePort, remoteHost, localPort);
			System.out.println("localhost:" + assinged_port + " -> " + remoteHost + ":" + remotePort);
			connect.setPort(localPort);
			LarkFrame.larkLog.receiveMsg(LogMsg.builder()
					.logType(LogType.INFO)
					.msgType(MsgType.RESPONSE)
					.msg("SSH通道已打开，SSH服务器版本：" + session.getServerVersion())
					.receiveTime(LocalDateTime.now())
					.server(connect.getName())
					.build());
			return session;
		}catch (JSchException e){
			LarkFrame.larkLog.receiveMsg(LogMsg.builder()
					.logType(LogType.ERROR)
					.msgType(MsgType.RESPONSE)
					.msg("SSH通道打开失败:"+e.getMessage())
					.receiveTime(LocalDateTime.now())
					.server(connect.getName())
					.build());
			throw e;
		}
	}


}
